抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

我们要得到的结果如封面所示

由于博客采用了图片懒加载(lazyload)插件,在加载dom后和图片加载成功之前,图片占位图为1px的像素,图片加载成功之后,则会把布局撑开,造成布局抖动。如果把和要加载的图片(本博客所有图片都放在了GitHub图床上,所以要用到image-size库访问远程图片)一样大小的占位图替代1px大小的占位图,则能解决布局抖动问题。本post将会占位图以硬编码的方式写到dom中,这需要修改在生成hexo博客时会用到的fancybox.js(或许每个主题用到的文件里都不一样,但最终都会使用hexo.extend.tag.register()方法;本博客使用volantis主题)。

为图片添加占位图的基本结构

以全宽图片为例:

1
2
3
4
5
6
7
8
9
<div style='position: relative; width: 100%;'>
<!-- padding-bottom设置占位图大小 -->
<a style="position: relative; height: 0; padding-bottom: ${h_divide_w_str};color:gray;background-color: rgb(204, 204, 204);">
<!-- 占位图中的文字 -->
<div style="position: absolute; left: 50%; top: 50%;transform: translate(-50%, -50%);">loading picture...</div>
<!-- 图片 -->
<img>
</a>
</div>

其中positionwidthheightpadding为必要属性,${h_divide_w_str}为占位图的宽高比。因为需要解决布局抖动问题,所以占位图的宽高比要与图片一致,如何动态获取宽高比呢?这需要在生成hexo博客之前获取,然后代入到${h_divide_w_str}中。

volantis主题动态获取远程图片的宽高比

有些时候可能读取不了图片就出错,可以试多几次就没有问题,或者自己写一个失败重试的代码

在编写博客的时候用到了gallery标签插件,这个插件可以在点击图片的时候放大(这是由fancybox.jsfancybox.css驱动。注意:这里的fancybox.jsJavaScript文件,而生成hexo博客用到的同名文件是Nodejs文件)

我们需要修改的,就是生成html对应的文件,而生成gallery的文件位于blog\node_modules\hexo-theme-volantis\scripts\tags\fancybox.js(nodejs文件)。

点击链接可以下载我修改好的fancybox.js

以下是使用image-size库访问远程图片得到宽高比的代码示例。

blog\node_modules\hexo-theme-volantis\scripts\tags\fancybox.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const url = require('url')
const http = require('http')

const sizeOf = require('image-size')

const imgUrl = 'http://my-amazing-website.com/image.jpeg'
const options = url.parse(imgUrl)

http.get(options, function (response) {
const chunks = []
response.on('data', function (chunk) {
chunks.push(chunk)
}).on('end', function() {
const buffer = Buffer.concat(chunks)
const dimensions = sizeOf(buffer);
const h_divide_w = 100 * dimensions.height / dimensions.width;
const h_divide_w_str = h_divide_w.toString() + '%'; //宽高比
})
})

修改宽高比

得到宽高比后自定义css样式,以下以volantis主题为例

blog\node_modules\hexo-theme-volantis\scripts\tags\fancybox.js
1
2
3
4
5
const result = `<div class='fancybox' style='position: relative; width: 100%;'>
<a class='fancybox' pjax-fancybox itemscope itemtype="http://schema.org/ImageObject" itemprop="url" href='${url}' data-fancybox='${group}' data-caption='${alt}' style="position: relative;background-color: rgb(204, 204, 204);border-radius: 4px; padding-bottom: ${h_divide_w_str};color:gray;">
<div style="position: absolute; left: 50%; top: 50%;transform: translate(-50%, -50%);">loading picture...</div>
${newItem}
</a>${buidAlt(imageTags || alt)}</div>`;

此代码只是实例,不同主题的class和变量名称可能不一样

因为是获取图片宽高比异步进行的,所以要在hexo.extend.tag.register()方法中添加async: true以堵塞博客的生成,这样才能在得到宽高比后生成博客。此方法点击以下链接查看更多。

blog\node_modules\hexo-theme-volantis\scripts\tags\fancybox.js
1
hexo.extend.tag.register('gallery', postFancybox, { ends: true ,async: true});

其中postFancybox是返回值是promise类型。(点击以上的hexo标签插件(Tag)查看更多)

如果在一个gallery内有多个图片,或许需要用到for循环,那么需要promise.all()确保所有占位图生成完成后再生成网页。

修改fancybox.css

hexocss是用styl编写的,而volantis主题的fancybox.css位于blog\node_modules\hexo-theme-volantis\source\css\_style\_tag-plugins\fancybox.styl

点击链接查看更多

主要就是把影响自定义cssheight属性删去(我大概也忘了修改过什么了,,,)。

添加动画

html代码添加到fancybox.js对应位置就能得到以上效果

blog\node_modules\hexo-theme-volantis\scripts\tags\fancybox.js
1
<svg class="spinner0" viewBox="0 0 50 50"> <circle class="path0" cx="25" cy="25" r="20" fill="none" stroke-width="4"></circle></svg>

然后修改css样式

blog\node_modules\hexo-theme-volantis\source\css\_first\base_first.styl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
.spinner0 {
animation: rotate 2s linear infinite;
position: absolute;
top: 50%;
left: 50%;
margin: -25px 0 0 -25px;
width: 50px;
height: 50px;

& .path0 {
stroke: var(--color-list-hl);
stroke-linecap: round;
animation: dash 1.5s ease-in-out infinite;
}

}

@keyframes rotate {
100% {
transform: rotate(360deg);
}
}

@keyframes dash {
0% {
stroke-dasharray: 1, 150;
stroke-dashoffset: 0;
}
50% {
stroke-dasharray: 90, 150;
stroke-dashoffset: -35;
}
100% {
stroke-dasharray: 90, 150;
stroke-dashoffset: -124;
}
}

// 加载图片后暂停动画
.spinner0:has(+ img.loaded){
animation-play-state: paused;
> circle {
animation-play-state: paused;
}
}

评论